#pragma once


namespace dotNetInteropPlugin {
	namespace PluginDev {
		using namespace System;
		using namespace System::Runtime::InteropServices;



		//private delegate void OnControllerEventDelegate(AIMP2CallbackType ClbTp);

		//class CtrlUnmanagedEventTranslator
		//{
		//public:
		//	CtrlUnmanagedEventTranslator(IAIMP2Controller* t, OnControllerEventDelegate^ Del) : Value(t)
		//	{
		//		bool R = false;
		//		CallDelegate = Del;
		//		R = t->AIMP_CallBack_Set((UInt32)AIMP2CallbackType::AIMP_EFFECT_CHANGED, CtrlClbFunc, (DWORD)this);
		//		R = t->AIMP_CallBack_Set((UInt32)AIMP2CallbackType::AIMP_EQ_CHANGED, CtrlClbFunc, (DWORD)this);
		//		R = t->AIMP_CallBack_Set((UInt32)AIMP2CallbackType::AIMP_INFO_UPDATE, CtrlClbFunc, (DWORD)this);
		//		R = t->AIMP_CallBack_Set((UInt32)AIMP2CallbackType::AIMP_PLAY_FILE, CtrlClbFunc, (DWORD)this);
		//		R = t->AIMP_CallBack_Set((UInt32)AIMP2CallbackType::AIMP_PLAYER_STATE, CtrlClbFunc, (DWORD)this);
		//		R = t->AIMP_CallBack_Set((UInt32)AIMP2CallbackType::AIMP_STATUS_CHANGE, CtrlClbFunc, (DWORD)this);
		//		R = t->AIMP_CallBack_Set((UInt32)AIMP2CallbackType::AIMP_TRACK_POS_CHANGED, CtrlClbFunc, (DWORD)this);
		//	}

		//	void UnsubscribeAllEvents()
		//	{
		//		Value->AIMP_CallBack_Remove((UInt32)AIMP2CallbackType::AIMP_EFFECT_CHANGED, CtrlClbFunc);
		//		Value->AIMP_CallBack_Remove((UInt32)AIMP2CallbackType::AIMP_EQ_CHANGED, CtrlClbFunc);
		//		Value->AIMP_CallBack_Remove((UInt32)AIMP2CallbackType::AIMP_INFO_UPDATE, CtrlClbFunc);
		//		Value->AIMP_CallBack_Remove((UInt32)AIMP2CallbackType::AIMP_PLAY_FILE, CtrlClbFunc);
		//		Value->AIMP_CallBack_Remove((UInt32)AIMP2CallbackType::AIMP_PLAYER_STATE, CtrlClbFunc);
		//		Value->AIMP_CallBack_Remove((UInt32)AIMP2CallbackType::AIMP_STATUS_CHANGE, CtrlClbFunc);
		//		Value->AIMP_CallBack_Remove((UInt32)AIMP2CallbackType::AIMP_TRACK_POS_CHANGED, CtrlClbFunc);
		//	}

		//	~CtrlUnmanagedEventTranslator()
		//	{
		//		Value->AIMP_CallBack_Remove((UInt32)AIMP2CallbackType::AIMP_EFFECT_CHANGED, CtrlClbFunc);
		//		Value->AIMP_CallBack_Remove((UInt32)AIMP2CallbackType::AIMP_EQ_CHANGED, CtrlClbFunc);
		//		Value->AIMP_CallBack_Remove((UInt32)AIMP2CallbackType::AIMP_INFO_UPDATE, CtrlClbFunc);
		//		Value->AIMP_CallBack_Remove((UInt32)AIMP2CallbackType::AIMP_PLAY_FILE, CtrlClbFunc);
		//		Value->AIMP_CallBack_Remove((UInt32)AIMP2CallbackType::AIMP_PLAYER_STATE, CtrlClbFunc);
		//		Value->AIMP_CallBack_Remove((UInt32)AIMP2CallbackType::AIMP_STATUS_CHANGE, CtrlClbFunc);
		//		Value->AIMP_CallBack_Remove((UInt32)AIMP2CallbackType::AIMP_TRACK_POS_CHANGED, CtrlClbFunc);
		//	}
		//protected:
		//	gcroot<OnControllerEventDelegate^> CallDelegate;
		//	IAIMP2Controller* Value;

		//	static void __stdcall CtrlClbFunc(DWORD User, DWORD dwCBType)
		//	{
		//		CtrlUnmanagedEventTranslator* cbTp = (CtrlUnmanagedEventTranslator*)User;
		//		(*cbTp).CallDelegate->Invoke(AIMP2CallbackType(dwCBType));
		//	}
		//};


		typedef void (__stdcall UnmanagedActionAIMPClbTp)(DWORD);
		private delegate void ActionAIMPClbTp(AIMP2CallbackType clbTp);

		class UnmanagedEventControllerHelper
		{
		public:
			UnmanagedEventControllerHelper(IAIMP2Controller* t)
			{
				AIMPCtrl = t;
				AIMPCtrl->AddRef();
			}

			void UnsubscribeAllEvents()
			{
				AIMPCtrl->AIMP_CallBack_Remove((UInt32)AIMP2CallbackType::AIMP_EFFECT_CHANGED, CtrlClbUniform);
				AIMPCtrl->AIMP_CallBack_Remove((UInt32)AIMP2CallbackType::AIMP_EQ_CHANGED, CtrlClbUniform);
				AIMPCtrl->AIMP_CallBack_Remove((UInt32)AIMP2CallbackType::AIMP_INFO_UPDATE, CtrlClbUniform);
				AIMPCtrl->AIMP_CallBack_Remove((UInt32)AIMP2CallbackType::AIMP_PLAY_FILE, CtrlClbUniform);
				AIMPCtrl->AIMP_CallBack_Remove((UInt32)AIMP2CallbackType::AIMP_PLAYER_STATE, CtrlClbUniform);
				AIMPCtrl->AIMP_CallBack_Remove((UInt32)AIMP2CallbackType::AIMP_STATUS_CHANGE, CtrlClbUniform);
				AIMPCtrl->AIMP_CallBack_Remove((UInt32)AIMP2CallbackType::AIMP_TRACK_POS_CHANGED, CtrlClbUniform);
			}

			bool SubscribeToEvent(AIMP2CallbackType clbType)
			{
				return AIMPCtrl->AIMP_CallBack_Set((UInt32)clbType, CtrlClbUniform, (DWORD)this);
			}

			bool UnsubscribeFromEvent(AIMP2CallbackType clbType)
			{
				return AIMPCtrl->AIMP_CallBack_Remove((UInt32)clbType, CtrlClbUniform);
			}

			~UnmanagedEventControllerHelper()
			{
				UnsubscribeAllEvents();
				if (AIMPCtrl != NULL)
				{
					AIMPCtrl->Release();
					AIMPCtrl = NULL;
				}
			}

		UnmanagedActionAIMPClbTp* UniformEventDelegate;


		private:
			IAIMP2Controller* AIMPCtrl;

			static void __stdcall CtrlClbUniform(DWORD User, DWORD dwCBType)
			{
				UnmanagedEventControllerHelper* cbTp = (UnmanagedEventControllerHelper*)User;
				(*cbTp).UniformEventDelegate(dwCBType);				
			}

		};


		//===================

		private ref class CentralizedEventController: public System::MarshalByRefObject
		{
		private:
			UnmanagedEventControllerHelper* inUnmanHelper;

			static CentralizedEventController^ inInst = nullptr;

			CentralizedEventController(IAIMP2Controller* Ctrl)
			{
				inUnmanHelper = new UnmanagedEventControllerHelper(Ctrl);

				inCallFromAimp = gcnew ActionAIMPClbTp(this, &CentralizedEventController::CallFromAimpFnc);
				inUnmanHelper->UniformEventDelegate = (UnmanagedActionAIMPClbTp*)(System::Runtime::InteropServices::Marshal::GetFunctionPointerForDelegate(inCallFromAimp).ToPointer());

			}

			~CentralizedEventController()
			{
				if (inUnmanHelper != NULL)
				{
					delete inUnmanHelper;
					inUnmanHelper = NULL;
				}
			}

			
		public:
			property static CentralizedEventController^ Instance
			{
				CentralizedEventController^ get()
				{
					return inInst;
				}
			}

			static void Initialize(IAIMP2Controller* Ctrl)
			{
				if (inInst != nullptr)
					delete inInst;
				inInst = gcnew CentralizedEventController(Ctrl);
			}

			static void Deinitialize()
			{
				if (inInst != nullptr)
				{
					delete inInst;
					inInst = nullptr;
				}
			}

			//=================================
		private:
			ActionAIMPClbTp^ inCallFromAimp;

			void CallFromAimpFnc(AIMP2CallbackType clbTp)
			{
				Action^ callAct = nullptr;
				switch (clbTp)
				{
				case AIMP2CallbackType::AIMP_STATUS_CHANGE:
					callAct = inSatusChangeEventDelegate;
					break;
				case AIMP2CallbackType::AIMP_PLAY_FILE:
					callAct = inPlayFileEventDelegate;
					break;
				case AIMP2CallbackType::AIMP_INFO_UPDATE:
					callAct = inInfoUpdateEventDelegate;
					break;
				case AIMP2CallbackType::AIMP_PLAYER_STATE:
					callAct = inPlayerStateChangedEventDelegate;
					break;
				case AIMP2CallbackType::AIMP_EFFECT_CHANGED:
					callAct = inEffectChangedEventDelegate;
					break;
				case AIMP2CallbackType::AIMP_EQ_CHANGED:
					callAct = inEQChangedEventDelegate;
					break;
				case AIMP2CallbackType::AIMP_TRACK_POS_CHANGED:
					callAct = inTrackPosChangedEventDelegate;
					break;
				}

				if (callAct != nullptr)
					callAct();
			}

		private:
			Action^ inSatusChangeEventDelegate;
			Action^ inPlayFileEventDelegate;
			Action^ inInfoUpdateEventDelegate;
			Action^ inPlayerStateChangedEventDelegate;
			Action^ inEffectChangedEventDelegate;
			Action^ inEQChangedEventDelegate;
			Action^ inTrackPosChangedEventDelegate;

		public:

			event Action^ SatusChangeEvent
			{
				[System::Runtime::CompilerServices::MethodImplAttribute(System::Runtime::CompilerServices::MethodImplOptions::Synchronized)] 
				void add(Action^ f)
				{
					bool Before = this->inSatusChangeEventDelegate == nullptr;
					this->inSatusChangeEventDelegate = (Action^) Delegate::Combine(this->inSatusChangeEventDelegate, f);
					if (Before && this->inSatusChangeEventDelegate != nullptr)
						inUnmanHelper->SubscribeToEvent(AIMP2CallbackType::AIMP_STATUS_CHANGE);
					//inUnmanHelper->StatusChangeEventDelegate = this->inSatusChangeEventDelegate;
				}
				[System::Runtime::CompilerServices::MethodImplAttribute(System::Runtime::CompilerServices::MethodImplOptions::Synchronized)]  
				void remove(Action^ f)
				{
					bool Before = this->inSatusChangeEventDelegate != nullptr;
					this->inSatusChangeEventDelegate = (Action^) Delegate::Remove(this->inSatusChangeEventDelegate, f);
					if (Before && this->inSatusChangeEventDelegate == nullptr)
						inUnmanHelper->UnsubscribeFromEvent(AIMP2CallbackType::AIMP_STATUS_CHANGE);
					//inUnmanHelper->StatusChangeEventDelegate = this->inSatusChangeEventDelegate;
				}
				void raise()
				{
					Action^ tmp = this->inSatusChangeEventDelegate;
					if (tmp != nullptr)
						tmp();
				}
			}

			//===============

			event Action^ PlayFileEvent
			{
				[System::Runtime::CompilerServices::MethodImplAttribute(System::Runtime::CompilerServices::MethodImplOptions::Synchronized)] 
				void add(Action^ f)
				{
					bool Before = this->inPlayFileEventDelegate == nullptr;
					this->inPlayFileEventDelegate = (Action^) Delegate::Combine(this->inPlayFileEventDelegate, f);
					if (Before && this->inPlayFileEventDelegate != nullptr)
						inUnmanHelper->SubscribeToEvent(AIMP2CallbackType::AIMP_PLAY_FILE);
					//inUnmanHelper->PlayFileEventDelegate = this->inPlayFileEventDelegate;
				}
				[System::Runtime::CompilerServices::MethodImplAttribute(System::Runtime::CompilerServices::MethodImplOptions::Synchronized)]  
				void remove(Action^ f)
				{
					bool Before = this->inPlayFileEventDelegate != nullptr;
					this->inPlayFileEventDelegate = (Action^) Delegate::Remove(this->inPlayFileEventDelegate, f);
					if (Before && this->inPlayFileEventDelegate == nullptr)
						inUnmanHelper->UnsubscribeFromEvent(AIMP2CallbackType::AIMP_PLAY_FILE);
					//inUnmanHelper->PlayFileEventDelegate = this->inPlayFileEventDelegate;
				}
				void raise()
				{
					Action^ tmp = this->inPlayFileEventDelegate;
					if (tmp != nullptr)
						tmp();
				}
			}

			//=====================================


			event Action^ InfoUpdateEvent
			{
				[System::Runtime::CompilerServices::MethodImplAttribute(System::Runtime::CompilerServices::MethodImplOptions::Synchronized)] 
				void add(Action^ f)
				{
					bool Before = this->inInfoUpdateEventDelegate == nullptr;
					this->inInfoUpdateEventDelegate = (Action^) Delegate::Combine(this->inInfoUpdateEventDelegate, f);
					if (Before && this->inInfoUpdateEventDelegate != nullptr)
						inUnmanHelper->SubscribeToEvent(AIMP2CallbackType::AIMP_INFO_UPDATE);
					//inUnmanHelper->InfoUpdateEventDelegate = this->inInfoUpdateEventDelegate;
				}
				[System::Runtime::CompilerServices::MethodImplAttribute(System::Runtime::CompilerServices::MethodImplOptions::Synchronized)]  
				void remove(Action^ f)
				{
					bool Before = this->inInfoUpdateEventDelegate != nullptr;
					this->inInfoUpdateEventDelegate = (Action^) Delegate::Remove(this->inInfoUpdateEventDelegate, f);
					if (Before && this->inInfoUpdateEventDelegate == nullptr)
						inUnmanHelper->UnsubscribeFromEvent(AIMP2CallbackType::AIMP_INFO_UPDATE);
					//inUnmanHelper->InfoUpdateEventDelegate = this->inInfoUpdateEventDelegate;
				}
				void raise()
				{
					Action^ tmp = this->inInfoUpdateEventDelegate;
					if (tmp != nullptr)
						tmp();
				}
			}

			//===============================


			event Action^ PlayerStateChangedEvent
			{
				[System::Runtime::CompilerServices::MethodImplAttribute(System::Runtime::CompilerServices::MethodImplOptions::Synchronized)] 
				void add(Action^ f)
				{
					bool Before = this->inPlayerStateChangedEventDelegate == nullptr;
					this->inPlayerStateChangedEventDelegate = (Action^) Delegate::Combine(this->inPlayerStateChangedEventDelegate, f);
					if (Before && this->inPlayerStateChangedEventDelegate != nullptr)
						inUnmanHelper->SubscribeToEvent(AIMP2CallbackType::AIMP_PLAYER_STATE);
					//inUnmanHelper->PlayerStateChangedEventDelegate = this->inPlayerStateChangedEventDelegate;
				}
				[System::Runtime::CompilerServices::MethodImplAttribute(System::Runtime::CompilerServices::MethodImplOptions::Synchronized)]  
				void remove(Action^ f)
				{
					bool Before = this->inPlayerStateChangedEventDelegate != nullptr;
					this->inPlayerStateChangedEventDelegate = (Action^) Delegate::Remove(this->inPlayerStateChangedEventDelegate, f);
					if (Before && this->inPlayerStateChangedEventDelegate == nullptr)
						inUnmanHelper->UnsubscribeFromEvent(AIMP2CallbackType::AIMP_PLAYER_STATE);
					//inUnmanHelper->PlayerStateChangedEventDelegate = this->inPlayerStateChangedEventDelegate;
				}
				void raise()
				{
					Action^ tmp = this->inPlayerStateChangedEventDelegate;
					if (tmp != nullptr)
						tmp();
				}
			}

			//=================================


			event Action^ EffectChangedEvent
			{
				[System::Runtime::CompilerServices::MethodImplAttribute(System::Runtime::CompilerServices::MethodImplOptions::Synchronized)] 
				void add(Action^ f)
				{
					bool Before = this->inEffectChangedEventDelegate == nullptr;
					this->inEffectChangedEventDelegate = (Action^) Delegate::Combine(this->inEffectChangedEventDelegate, f);
					if (Before && this->inEffectChangedEventDelegate != nullptr)
						inUnmanHelper->SubscribeToEvent(AIMP2CallbackType::AIMP_EFFECT_CHANGED);
					//inUnmanHelper->EffectChangedEventDelegate = this->inEffectChangedEventDelegate;
				}
				[System::Runtime::CompilerServices::MethodImplAttribute(System::Runtime::CompilerServices::MethodImplOptions::Synchronized)]  
				void remove(Action^ f)
				{
					bool Before = this->inEffectChangedEventDelegate != nullptr;
					this->inEffectChangedEventDelegate = (Action^) Delegate::Remove(this->inEffectChangedEventDelegate, f);
					if (Before && this->inEffectChangedEventDelegate == nullptr)
						inUnmanHelper->UnsubscribeFromEvent(AIMP2CallbackType::AIMP_EFFECT_CHANGED);
					//inUnmanHelper->EffectChangedEventDelegate = this->inEffectChangedEventDelegate;
				}
				void raise()
				{
					Action^ tmp = this->inEffectChangedEventDelegate;
					if (tmp != nullptr)
						tmp();
				}
			}

			//==========================================

			event Action^ EQChangedEvent
			{
				[System::Runtime::CompilerServices::MethodImplAttribute(System::Runtime::CompilerServices::MethodImplOptions::Synchronized)] 
				void add(Action^ f)
				{
					bool Before = this->inEQChangedEventDelegate == nullptr;
					this->inEQChangedEventDelegate = (Action^) Delegate::Combine(this->inEQChangedEventDelegate, f);
					if (Before && this->inEQChangedEventDelegate != nullptr)
						inUnmanHelper->SubscribeToEvent(AIMP2CallbackType::AIMP_EQ_CHANGED);
					//inUnmanHelper->EQChangedEventDelegate = this->inEQChangedEventDelegate;
				}
				[System::Runtime::CompilerServices::MethodImplAttribute(System::Runtime::CompilerServices::MethodImplOptions::Synchronized)]  
				void remove(Action^ f)
				{
					bool Before = this->inEQChangedEventDelegate != nullptr;
					this->inEQChangedEventDelegate = (Action^) Delegate::Remove(this->inEQChangedEventDelegate, f);
					if (Before && this->inEQChangedEventDelegate == nullptr)
						inUnmanHelper->UnsubscribeFromEvent(AIMP2CallbackType::AIMP_EQ_CHANGED);
					//inUnmanHelper->EQChangedEventDelegate = this->inEQChangedEventDelegate;
				}
				void raise()
				{
					Action^ tmp = this->inEQChangedEventDelegate;
					if (tmp != nullptr)
						tmp();
				}
			}

			//==================================


			event Action^ TrackPosChangedEvent
			{
				[System::Runtime::CompilerServices::MethodImplAttribute(System::Runtime::CompilerServices::MethodImplOptions::Synchronized)] 
				void add(Action^ f)
				{
					bool Before = this->inTrackPosChangedEventDelegate == nullptr;
					this->inTrackPosChangedEventDelegate = (Action^) Delegate::Combine(this->inTrackPosChangedEventDelegate, f);
					if (Before && this->inTrackPosChangedEventDelegate != nullptr)
						inUnmanHelper->SubscribeToEvent(AIMP2CallbackType::AIMP_TRACK_POS_CHANGED);
					//inUnmanHelper->TrackPosChangedEventDelegate = this->inTrackPosChangedEventDelegate;
				}
				[System::Runtime::CompilerServices::MethodImplAttribute(System::Runtime::CompilerServices::MethodImplOptions::Synchronized)]  
				void remove(Action^ f)
				{
					bool Before = this->inTrackPosChangedEventDelegate != nullptr;
					this->inTrackPosChangedEventDelegate = (Action^) Delegate::Remove(this->inTrackPosChangedEventDelegate, f);
					if (Before && this->inTrackPosChangedEventDelegate == nullptr)
						inUnmanHelper->UnsubscribeFromEvent(AIMP2CallbackType::AIMP_TRACK_POS_CHANGED);
					//inUnmanHelper->TrackPosChangedEventDelegate = this->inTrackPosChangedEventDelegate;
				}
				void raise()
				{
					Action^ tmp = this->inTrackPosChangedEventDelegate;
					if (tmp != nullptr)
						tmp();
				}
			}
		};


	}
}